package AnalizadorSintactico;

import AnalizadorLexico.CategoriaLexica;
import AnalizadorLexico.Lexema;

import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Stack;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

/**
 * @author Juan Pablo
 * @author esteban
 * @author Johan
 */
public class AnalizadorSintactico implements PalabraReservada, CategoriaLexica {
	// atributos de clasePalabraReservada

	private ArrayList<Lexema> tablaDeSimbolos;
	private Lexema tokenActual;
	private int posicion;
	private Stack pila;
	private ArrayList<String> errores;
	private DefaultMutableTreeNode unidadDeCompilacion;
	private String ambito;
	private String clase;
	private UnidadCompilacion uc;

	/**
	 * inicializar el analizador sintactico
	 */
	public AnalizadorSintactico() {
		posicion = 0;
		pila = new Stack();
		errores = new ArrayList<String>();
	}

	/**
	 * metodo encargado de analizar el codigo y retornar un arbol sintactico
	 *
	 * @param arreglo
	 */
	public void analizar(ArrayList<Lexema> arreglo) {
		this.tablaDeSimbolos = arreglo;
		posicion = 0;
		tokenActual = tablaDeSimbolos.get(0);
		identificarPalabrasReservadas();
		uc = esUnidadDeCompilacion();
		unidadDeCompilacion = uc.getSubArbol();

		/*if (uc != null) {
			JPanel panel = new JPanel();
			JScrollPane scrpn = new JScrollPane(panel);
			panel.setLayout(new BorderLayout());
			DefaultTreeModel modelo = new DefaultTreeModel(uc.getSubArbol());
			JTree arbol = new JTree(modelo);
			panel.add(arbol);
			JFrame ventana = new JFrame();
			ventana.setSize(400, 400);
			ventana.setContentPane(scrpn); //
			ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			ventana.setLocationRelativeTo(null);
			ventana.setVisible(true);
		}*/

		/*
		 * ExpresionLogica ex = esExpresionLogica(); System.out.println("exp: " +
		 * (ex != null ? "si" : "no"));
		 */

	}

	/**
	 * metodo para identificar las palabras reservadas del lenguaje
	 */
	public void identificarPalabrasReservadas() {
		Lexema token;
		for (int i = 0; i < tablaDeSimbolos.size(); i++) {
			token = tablaDeSimbolos.get(i);
			if (UtilidadesSintactico.esPalabraReservada(token)) {
				token.setTipo(token.getLexema());
			}
		}
	}

	/**
	 * metodo predicado que identifica una unidad de compilacion.
	 *
	 * @return
	 */
	public UnidadCompilacion esUnidadDeCompilacion() {
		Paquete paquete = esPaquete();
		ArrayList<Importacion> importaciones = sonImportaciones(new ArrayList<Importacion>());
		DeclaracionDeClaseOInterfaz dcoi = esDeclaracionDeClaseOInterfaz();
		if (dcoi != null) {
			UnidadCompilacion u = new UnidadCompilacion(paquete, importaciones,
					dcoi);
			return u;
		} else {
			reportarError("falta declaracion de clase o interfaz", tokenActual
					.getFila(), tokenActual.getColumna());
		}
		return null;
	}

	/**
	 * metodo predicado que identifica si es una declaracion de una clase o una
	 * declaracion de interfaz
	 *
	 * @return
	 */
	private DeclaracionDeClaseOInterfaz esDeclaracionDeClaseOInterfaz() {
		DeclaracionDeClase dc = esDeclaracionDeClase();
		if (dc != null) {
			DeclaracionDeClaseOInterfaz dcoi = new DeclaracionDeClaseOInterfaz(
					dc);
			return dcoi;
		} else {
			DeclaracionDeInterfaz di = esDeclaracionDeInterfaz();
			if (di != null) {
				DeclaracionDeClaseOInterfaz dcoi = new DeclaracionDeClaseOInterfaz(
						di);
				return dcoi;
			}
		}
		return null;
	}

	/**
	 * metodo predicado que identifica si es una declaracion de interfaz
	 *
	 * @return
	 */
	private DeclaracionDeInterfaz esDeclaracionDeInterfaz() {
		Lexema acceso = null;
		Lexema idClase = null;
		Lexema padre = null;
		// bloque de interfaz
		esSeparadorSentencias();
		guardarTokenInicio();
		// si hay un modificador de acceso
		if (tokenActual.getTipo().equals(PUBLIC)
				|| tokenActual.getTipo().equals(PRIVATE)) {
			acceso = tokenActual;
			siguienteToken();
		}
		if (tokenActual.getTipo().equals(INTERFACE)) {
			siguienteToken();
			if (tokenActual.getTipo().equals(IDCLASE)) {
				idClase = tokenActual;
				siguienteToken();
				// herencia
				if (tokenActual.getTipo().equals(EXTENDS)) {
					siguienteToken();
					if (tokenActual.getTipo().equals(IDCLASE)) {
						System.out.println("idClasePadre: "
								+ tokenActual.getLexema());
						padre = tokenActual;
						siguienteToken();
					}
				}
				esSeparadorSentencias();
				// BloquedeClase
				// reconocer el bloque de interfaz
				if (tokenActual.getTipo().equals(ENDINTERFACE)) {
					DeclaracionDeInterfaz Interfaz = new DeclaracionDeInterfaz(
							acceso, idClase, padre);
					return Interfaz;
				}
			}
		} else {
			reportarError("Falta palabra reservada class o interface",
					tokenActual.getFila(), tokenActual.getColumna());
		}

		return null;
	}

	/**
	 * metodo predicado que identifica si es una declaracion de clase
	 *
	 * @return
	 */
	private DeclaracionDeClase esDeclaracionDeClase() {
		Lexema acceso = null;
		Lexema idClase = null;
		Lexema padre = null;
		ArrayList<Lexema> implementaciones = null;
		BloqueDeClase bc = null;
		guardarTokenInicio();
		esSeparadorSentencias();
		guardarTokenInicio();
		// si hay un modificador de acceso
		if (tokenActual.getTipo().equals(PUBLIC)
				|| tokenActual.getTipo().equals(PRIVATE)) {
			acceso = tokenActual;
			siguienteToken();
		}
		if (tokenActual.getTipo().equals(CLASS)) {
			siguienteToken();
			if (tokenActual.getTipo().equals(IDCLASE)) {
				idClase = tokenActual;
				clase = idClase.getLexema();
				ambito = tokenActual.getLexema();// obtenemos el ambito
				// actual para los campos de
				// la clase
				siguienteToken();
				// herencia
				if (tokenActual.getTipo().equals(EXTENDS)) {
					siguienteToken();
					if (tokenActual.getTipo().equals(IDCLASE)) {
						System.out.println("idClasePadre: "
								+ tokenActual.getLexema());
						padre = tokenActual;
						siguienteToken();
					}
				}
				// implementaciones
				if (tokenActual.getTipo().equals(IMPLEMENTS)) {
					siguienteToken();
					implementaciones = sonImplementaciones(new ArrayList<Lexema>());
					if (implementaciones != null) {
					}
				}
				esSeparadorSentencias();
				// BloquedeClase
				bc = esBloqueDeClase();
				if (tokenActual.getTipo().equals(ENDCLASS)) {
					DeclaracionDeClase clase = new DeclaracionDeClase(acceso,
							idClase, padre, bc, implementaciones);
					return clase;
				} else {
					reportarError("Falta palabra reservada endClass",
							tokenActual.getFila(), tokenActual.getColumna());
					DeclaracionDeClase clase = new DeclaracionDeClase(acceso,
							idClase, padre, bc, implementaciones);
					return clase;
				}
			} else {
				reportarError("Falta Identificador de Clase", tokenActual
						.getFila(), tokenActual.getColumna());
			}
		} else {
			recuperarTokenInicio();
		}

		return null;
	}

	/**
	 * metodo predicado que comprueba si se acepta un bloque de clase
	 *
	 * @return
	 */
	private BloqueDeClase esBloqueDeClase() {
		ArrayList<Campo> bloqueCampos = esBloqueCampos();
		ArrayList<Metodo> bloqueMetodos = esBloqueMetodos();
		if (bloqueCampos == null) {
			bloqueCampos = esBloqueCampos();
		}
		BloqueDeClase bc = new BloqueDeClase(bloqueCampos, bloqueMetodos);
		return bc;
	}

	/**
	 * metodo predicado que identifica si es un bloque de metodos
	 *
	 * @return
	 */
	private ArrayList<Metodo> esBloqueMetodos() {
		if (tokenActual.getTipo().equals(METHODSBLOCK)) {
			siguienteToken();
			if (esSeparadorSentencias()) {
				ArrayList<Metodo> metodos = sonMetodos(new ArrayList<Metodo>());
				if (tokenActual.getTipo().equals(ENDMETHODSBLOCK)) {
					siguienteToken();
					esSeparadorSentencias();
					return metodos;
				} else {
					reportarError("falta palabra reservada  "
							+ tokenActual.getLexema() + " " + ENDMETHODSBLOCK,
							tokenActual.getFila(), tokenActual.getColumna());
					siguienteToken();
					esSeparadorSentencias();
					return metodos;
				}
			}
		}
		return null;
	}

	/**
	 * metodo predicado que identifica si un conjunto de tokens son metodos
	 *
	 * @param metodos
	 * @return
	 */
	private ArrayList<Metodo> sonMetodos(ArrayList<Metodo> metodos) {
		Lexema acceso = null;
		Lexema modificador = null;
		Lexema tipoRetorno = null;
		Lexema idMetodo = null;
		ArrayList<Parametro> parametros = null;
		ArrayList<Sentencia> bloqueSentencias = null;
		if (tokenActual.getTipo().equals(PUBLIC)
				|| tokenActual.getTipo().equals(PRIVATE)
				|| tokenActual.getTipo().equals(PROTECTED)) {
			acceso = tokenActual;
			siguienteToken();
		}
		if (tokenActual.getTipo().equals(STATIC)) {
			modificador = tokenActual;
			siguienteToken();
		}
		String tipo = tokenActual.getTipo();
		if (tipo.equals(IDCLASE) || tipo.equals(INT) || tipo.equals(DOUBLE)
				|| tipo.equals(BOOL) || tipo.equals(STRING)
				|| tipo.equals(VOID)) {
			tipoRetorno = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
				idMetodo = tokenActual;
				ambito = idMetodo.getLexema();// colocamos el ambito para cada
				// metodo
				siguienteToken();
				if (tokenActual.getLexema().equals("(")) {
					siguienteToken();
					parametros = sonParametros(new ArrayList<Parametro>());
					if (tokenActual.getLexema().equals(")")) {
						siguienteToken();
						esSeparadorSentencias();
						bloqueSentencias = esBloqueSentencias(new ArrayList<Sentencia>());
						if (tokenActual.getTipo().equals(ENDMETHOD)) {
							siguienteToken();
							esSeparadorSentencias();
							Metodo metodo = new Metodo(acceso, modificador,
									tipoRetorno, idMetodo, parametros,
									bloqueSentencias);
							metodos.add(metodo);
							return sonMetodos(metodos);
						} else {
							reportarError("falta palabra reservada "
									+ tokenActual.getLexema() + "" + ENDMETHOD,
									tokenActual.getFila(), tokenActual
											.getColumna());
							siguienteToken();
							esSeparadorSentencias();
							Metodo metodo = new Metodo(acceso, modificador,
									tipoRetorno, idMetodo, parametros,
									bloqueSentencias);
							metodos.add(metodo);
							return sonMetodos(metodos);
						}
					} else {
						reportarError("Se esperaba )", tokenActual.getFila(),
								tokenActual.getColumna());
					}
				} else {
					reportarError("Se esperaba (", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				reportarError("falta Identificador", tokenActual.getFila(),
						tokenActual.getColumna());
			}
		} else {
			// reportarError("falta tipo de Retorno", tokenActual.getFila(),
			// tokenActual.getColumna());
		}
		if (metodos.size() > 0) {
			return metodos;
		} else {
			return null;
		}
	}

	/**
	 * metodo predicado que identifica si es un bloque de sentencias
	 *
	 * @param bloqueSentencias
	 * @return
	 */
	private ArrayList<Sentencia> esBloqueSentencias(
			ArrayList<Sentencia> bloqueSentencias) {
		esSeparadorSentencias();
		Sentencia sentencia = esSentencia();
		if (sentencia != null) {
			bloqueSentencias.add(sentencia);
			return esBloqueSentencias(bloqueSentencias);
		}
		return bloqueSentencias;
	}

	private Sentencia esSentencia() {
		Sentencia sentencia;
		sentencia = esDecision();
		if (sentencia != null) {
			return sentencia;
		}
		sentencia = esCicloFor();
		if (sentencia != null) {
			return sentencia;
		}
		sentencia = esCicloWhile();
		if (sentencia != null) {
			return sentencia;
		}
		sentencia = esCicloDo();
		if (sentencia != null) {
			return sentencia;
		}
		sentencia = esBloqueTry();
		if (sentencia != null) {
			return sentencia;
		}
		guardarTokenInicio();
		sentencia = esLlamadoAMetodo();
		if (sentencia != null) {
			return sentencia;
		} else {
			recuperarTokenInicio();
		}
		guardarTokenInicio();
		sentencia = esIncrementoODecremento();
		if (sentencia != null) {
			return sentencia;
		} else {
			recuperarTokenInicio();
		}

		guardarTokenInicio();
		sentencia = esDeclaracionDeVariable();
		if (sentencia != null) {
			return sentencia;
		} else {
			recuperarTokenInicio();
		}
		guardarTokenInicio();
		sentencia = esAsignacion();
		if (sentencia != null) {
			return sentencia;
		} else {
			recuperarTokenInicio();
		}
		sentencia = esBreak();
		if (sentencia != null) {
			return sentencia;
		}
		sentencia = esContinue();
		if (sentencia != null) {
			return sentencia;
		}
		sentencia = esReturn();
		if (sentencia != null) {
			return sentencia;
		}
		return null;
	}

	private Sentencia esDeclaracionDeVariable() {
		Lexema variable = null;
		Lexema tipo = null;
		Expresion exp = null;
		String td = tokenActual.getTipo();
		if (td.equals(IDCLASE) || td.equals(INT) || td.equals(DOUBLE)
				|| td.equals(BOOL) || td.equals(STRING) || td.equals(IDCLASE)) {
			tipo = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
				variable = tokenActual;
				siguienteToken();
				if (tokenActual.getLexema().equals("=")) {
					siguienteToken();
					exp = esExpresion();
					if (exp != null) {
						esSeparadorSentencias();
						return new DeclaracionVariable(tipo, variable, exp,
								ambito);
					} else {
						reportarError("Falta expresion", tokenActual.getFila(),
								tokenActual.getColumna());
					}
				} else {
					esSeparadorSentencias();
					return new DeclaracionVariable(tipo, variable, null, ambito);
				}
			} else {
				reportarError("Falta Identificador", tokenActual.getFila(),
						tokenActual.getColumna());
			}
		}
		return null;
	}

	private Sentencia esReturn() {
		Lexema retorno;
		Expresion expRetorno;
		if (tokenActual.getTipo().equals(RETURN)) {
			siguienteToken();
			if (tokenActual.getTipo().equals(NULL)) {
				retorno = tokenActual;
				siguienteToken();
				esSeparadorSentencias();
				return new Return(retorno);
			}
			Expresion exp = esExpresion();
			if (exp != null) {
				return new Return(exp);
			} else {
				reportarError("falta expresion", tokenActual.getFila(),
						tokenActual.getColumna());
			}

		}

		return null;
	}

	private Sentencia esContinue() {
		if (tokenActual.getTipo().equals(CONTINUE)) {
			siguienteToken();
			return new Continue();
		}
		return null;
	}

	private Sentencia esBreak() {
		if (tokenActual.getTipo().equals(BREAK)) {
			siguienteToken();
			return new Break();
		}
		return null;
	}

	private Asignacion esAsignacion() {
		Lexema id = null;
		Lexema asignacion = null;
		Expresion exp = null;
		if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
			id = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(ASIGNACION)) {
				asignacion = tokenActual;
				siguienteToken();
				exp = esExpresion();
				if (exp != null) {
					if (esSeparadorSentencias()) {
						return new Asignacion(id, asignacion, exp);
					}
				} else {
					reportarError("falta una expresion", tokenActual.getFila(),
							tokenActual.getColumna());
				}

			}
		}
		return null;
	}

	private Expresion esExpresion() {
		ExpresionAritmetica expA = null;
		ExpresionLogica expL = null;
		ExpresionRelacional expR = null;
		LlamadaAMetodo llamada = null;
		IncrementoODecremento iod = null;
		Lexema idOcadena = null;
		Instanciacion ins = null;

		guardarTokenInicio();

		ins = esInstanciacion();

		if (ins != null) {
			return new Expresion(ins);
		} else {
			recuperarTokenInicio();
		}
		guardarTokenInicio();
		llamada = esLlamadoAMetodo();
		if (llamada != null) {
			return new Expresion(llamada);
		} else {
			recuperarTokenInicio();
		}
		guardarTokenInicio();
		expA = esExpresionAritmetica();
		if (expA != null) {
			return new Expresion(expA);
		} else {
			recuperarTokenInicio();
		}
		if (tokenActual.getTipo().equals(CADENA)
				|| tokenActual.getTipo().equals(IDENTIFICADOR)) {
			idOcadena = tokenActual;
			siguienteToken();
			return new Expresion(idOcadena);
		}
		return null;
	}

	private Instanciacion esInstanciacion() {
		ArrayList<Lexema> parametros = null;
		if (tokenActual.getTipo().equals(NEW)) {
			siguienteToken();
			if (tokenActual.getLexema().equals("(")) {
				siguienteToken();
				parametros = esPasoDeParametros(new ArrayList<Lexema>());
				if (tokenActual.getLexema().equals(")")) {
					siguienteToken();
					return new Instanciacion(parametros);
				} else {
					reportarError("Se esperaba )", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			}
		}
		return null;
	}

	private LlamadaAMetodo esLlamadoAMetodo() {
		guardarTokenInicio();
		Lexema nombreMetodo = null;
		Lexema nombreObjeto = null;

		guardarTokenInicio();
		if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
			nombreObjeto = tokenActual;
			siguienteToken();
			if (tokenActual.getLexema().equals(".")) {
				siguienteToken();
			} else {
				recuperarTokenInicio();
			}
		}
		if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
			nombreMetodo = tokenActual;
			siguienteToken();
			if (tokenActual.getLexema().equals("(")) {
				siguienteToken();
				ArrayList<Lexema> parametros = esPasoDeParametros(new ArrayList<Lexema>());
				if (tokenActual.getLexema().equals(")")) {
					siguienteToken();
					// esSeparadorSentencias();
					return new LlamadaAMetodo(nombreObjeto, nombreMetodo,
							parametros);
				} else {
					reportarError("Se esperaba )", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				recuperarTokenInicio();
				return null;
			}
		}

		return null;
	}

	private ArrayList<Lexema> esPasoDeParametros(ArrayList<Lexema> parametros) {
		if (tokenActual.getTipo().equals(IDENTIFICADOR)
				|| tokenActual.getTipo().equals(NUMENTERO)
				|| tokenActual.getTipo().equals(NUMREAL)) {
			parametros.add(tokenActual);
			siguienteToken();
			if (tokenActual.getLexema().equals(",")) {
				siguienteToken();
				return esPasoDeParametros(parametros);
			}
			return parametros;
		}
		return null;
	}

	private IncrementoODecremento esIncrementoODecremento() {
		Lexema iod = null;
		Lexema id = null;
		if (tokenActual.getTipo().equals(OPERADORINCREMENTO)
				|| tokenActual.getTipo().equals(OPERADORDECREMENTO)) {
			iod = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
				id = tokenActual;
				siguienteToken();
				return new IncrementoODecremento(iod, id, 0);
			}
		}
		if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
			id = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(OPERADORINCREMENTO)
					|| tokenActual.getTipo().equals(OPERADORDECREMENTO)) {
				iod = tokenActual;
				siguienteToken();
				return new IncrementoODecremento(iod, id, 1);
			}
		}
		return null;
	}

	private BloqueTryCatch esBloqueTry() {

		Lexema tipoExcepcion = null;
		Lexema identificador = null;
		ArrayList<Sentencia> sentenciasTry = null;
		ArrayList<Sentencia> sentenciasCatch = null;
		ArrayList<Sentencia> sentenciasFinally = null;
		if (tokenActual.getTipo().equals(TRY)) {
			siguienteToken();
			esSeparadorSentencias();
			sentenciasTry = esBloqueSentencias(new ArrayList<Sentencia>());
			if (tokenActual.getTipo().equals(CATCH)) {
				siguienteToken();
				if (tokenActual.getLexema().equals("(")) {
					siguienteToken();
					if (tokenActual.getTipo().equals(IDCLASE)) {
						tipoExcepcion = tokenActual;
						siguienteToken();
						if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
							identificador = tokenActual;
							siguienteToken();
							if (tokenActual.getLexema().equals(")")) {
								siguienteToken();
								esSeparadorSentencias();
								sentenciasCatch = esBloqueSentencias(new ArrayList<Sentencia>());
								if (tokenActual.getLexema().equals(FINALLY)) {
									siguienteToken();
									esSeparadorSentencias();
									sentenciasFinally = esBloqueSentencias(new ArrayList<Sentencia>());
								}
								if (tokenActual.getLexema().equals(ENDTRY)) {
									siguienteToken();
									esSeparadorSentencias();
									return new BloqueTryCatch(sentenciasTry,
											tipoExcepcion, identificador,
											sentenciasCatch, sentenciasFinally);
								}
							}
						}
					}
				}
			} else {
				reportarError("falta palabra reservada " + CATCH, tokenActual
						.getFila(), tokenActual.getColumna());
			}
		}
		return null;
	}

	private CicloHagaMientras esCicloDo() {
		Condicion condicion = null;
		;
		ArrayList<Sentencia> sentencias = null;
		if (tokenActual.getTipo().equals(DO)) {
			siguienteToken();
			esSeparadorSentencias();
			sentencias = esBloqueSentencias(new ArrayList<Sentencia>());
			if (tokenActual.getTipo().equals(WHILE)) {
				siguienteToken();
				if (tokenActual.getLexema().equals("(")) {
					siguienteToken();
					condicion = esCondicion();
					if (condicion != null) {
						if (tokenActual.getLexema().equals(")")) {
							siguienteToken();
							if (tokenActual.getLexema().equals(ENDWHILE)) {
								siguienteToken();
								esSeparadorSentencias();
								return new CicloHagaMientras(sentencias,
										condicion);
							} else {
								reportarError(
										"Se esperaba palabra reservada endwhile",
										tokenActual.getFila(), tokenActual
												.getColumna());
							}
						} else {
							reportarError("Se esperaba )", tokenActual
									.getFila(), tokenActual.getColumna());
						}
					} else {
						reportarError("Se esperaba una condicion valida",
								tokenActual.getFila(), tokenActual.getColumna());
					}
				}
			} else {
				reportarError("Se esperaba palabra reservada while",
						tokenActual.getFila(), tokenActual.getColumna());
			}
		}
		return null;
	}

	private CicloMientras esCicloWhile() {
		Condicion condicion = null;
		guardarTokenInicio();
		ArrayList<Sentencia> sentencias = null;
		if (tokenActual.getTipo().equals(WHILE)) {
			siguienteToken();
			if (tokenActual.getLexema().equals("(")) {
				siguienteToken();
				condicion = esCondicion();
				if (condicion != null) {
					if (tokenActual.getLexema().equals(")")) {
						siguienteToken();
						if (esSeparadorSentencias() == false) {
							recuperarTokenInicio();
						}
						sentencias = esBloqueSentencias(new ArrayList<Sentencia>());
						if (tokenActual.getLexema().equals(ENDWHILE)) {
							siguienteToken();
							esSeparadorSentencias();
							return new CicloMientras(condicion, sentencias);
						} else {
							reportarError(
									"Se esperaba palabra reservada endwhile",
									tokenActual.getFila(), tokenActual
											.getColumna());
						}
					} else {
						reportarError("Se esperaba )", tokenActual.getFila(),
								tokenActual.getColumna());
					}
				} else {
					reportarError("Se esperaba una condicion valida",
							tokenActual.getFila(), tokenActual.getColumna());
				}
			}
		}
		recuperarTokenInicio();
		return null;
	}

	private Sentencia esCicloFor() {
		Lexema identificador = null;
		Lexema inicio = null;
		Lexema fin = null;
		ArrayList<Sentencia> bloqueSentencias = null;
		if (tokenActual.getTipo().equals(FOR)) {
			siguienteToken();
			if (tokenActual.getLexema().equals(":")) {
				siguienteToken();
				if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
					identificador = tokenActual;
					siguienteToken();
					if (tokenActual.getLexema().equals(",")) {
						siguienteToken();
						if (tokenActual.getTipo().equals(IDENTIFICADOR)
								|| tokenActual.getTipo().equals(NUMENTERO)) {
							inicio = tokenActual;
							siguienteToken();
							if (tokenActual.getTipo().equals(UNTIL)) {
								siguienteToken();
								if (tokenActual.getTipo().equals(IDENTIFICADOR)
										|| tokenActual.getTipo().equals(
												NUMENTERO)) {
									fin = tokenActual;
									siguienteToken();
									esSeparadorSentencias();
									bloqueSentencias = esBloqueSentencias(new ArrayList<Sentencia>());
									if (tokenActual.getTipo().equals(ENDFOR)) {
										siguienteToken();
										esSeparadorSentencias();
										return new CicloPara(identificador,
												inicio, fin, bloqueSentencias);
									} else {
										reportarError(
												"Se esperaba palabra reservada endfor",
												tokenActual.getFila(),
												tokenActual.getColumna());
									}
								} else {
									reportarError(
											"Se esperaba un entero o identificador",
											tokenActual.getFila(), tokenActual
													.getColumna());
								}
							} else {
								reportarError(
										"Se esperaba palabra reservada until",
										tokenActual.getFila(), tokenActual
												.getColumna());
							}
						} else {
							reportarError(
									"Se esperaba un entero o identificador",
									tokenActual.getFila(), tokenActual
											.getColumna());
						}
					} else {
						reportarError("Se esperaba ,", tokenActual.getFila(),
								tokenActual.getColumna());
					}
				} else {
					reportarError("Se esperaba un identificador", tokenActual
							.getFila(), tokenActual.getColumna());
				}
			} else {
				reportarError("Se esperaba :", tokenActual.getFila(),
						tokenActual.getColumna());
			}
		}
		return null;
	}

	private Decision esDecision() {
		Condicion condicion = null;
		ArrayList<Sentencia> bloqueSentencias = null;
		ArrayList<Sentencia> bloqueSentenciasElse = null;
		if (tokenActual.getTipo().equals(IF)) {
			siguienteToken();
			if (tokenActual.getLexema().equals("(")) {
				siguienteToken();
				condicion = esCondicion();
				if (condicion != null) {
					// siguienteToken();
					if (tokenActual.getLexema().equals(")")) {
						siguienteToken();
						esSeparadorSentencias();
						bloqueSentencias = esBloqueSentencias(new ArrayList<Sentencia>());
						// siguienteToken();
						if (tokenActual.getLexema().equals(ELSE)) {
							siguienteToken();
							esSeparadorSentencias();
							bloqueSentenciasElse = esBloqueSentencias(new ArrayList<Sentencia>());
							// siguienteToken();
						}
						if (tokenActual.getTipo().equals(ENDIF)) {
							siguienteToken();
							esSeparadorSentencias();
							return new Decision(condicion, bloqueSentencias,
									bloqueSentenciasElse);
						} else {
							reportarError("falta palabra reservada " + ENDIF,
									tokenActual.getFila(), tokenActual
											.getColumna());
						}
					} else {
						reportarError("Se esperaba )", tokenActual.getFila(),
								tokenActual.getColumna());
					}
				} else {
					reportarError("Se esperaba una condicion", tokenActual
							.getFila(), tokenActual.getColumna());
				}
			}
		}
		return null;
	}

	private Condicion esCondicion() {
		ExpresionLogica expLogica = null;
		ExpresionRelacional expR = null;
		LlamadaAMetodo llamada = null;
		Lexema id = null;
		expLogica = esExpresionLogica();
		if (expLogica != null) {
			return new Condicion(expLogica);
		}
		expR = esExpresionRelacional();
		if (expLogica != null) {
			return new Condicion(expR);
		}
		llamada = esLlamadoAMetodo();
		if (llamada != null) {
			return new Condicion(llamada);
		}
		if (id.getTipo().equals(TRUE) || id.getTipo().equals(FALSE)) {
			return new Condicion(id);
		}
		return null;
	}

	private ExpresionLogica esExpresionLogica() {
		Lexema op = null;
		TerminoExpLogica b = esTerminoExpLogica();
		if (b != null) {
			if (tokenActual.getLexema().equals("|")
					|| tokenActual.getLexema().equals("&")) {
				op = tokenActual;
				siguienteToken();
				ExpresionLogica exp = esExpresionLogica();
				if (exp != null) {
					return new ExpresionLogica(b, op, exp);
				}
			}
			return new ExpresionLogica(b, null, null);
		}

		if (tokenActual.getLexema().equals("!")) {
			op = tokenActual;
			siguienteToken();
			ExpresionLogica exp = esExpresionLogica();
			if (exp != null) {
				return new ExpresionLogica(null, op, exp);
			} else {
				reportarError("falta expresion logica", tokenActual.getFila(),
						tokenActual.getColumna());
			}
		}
		return null;
	}

	private TerminoExpLogica esTerminoExpLogica() {
		if (tokenActual.getLexema().equals(TRUE)
				|| tokenActual.getLexema().equals(FALSE)) {
			Lexema bool = tokenActual;
			siguienteToken();
			return new TerminoExpLogica(bool, null, null, null, null);
		}

		guardarTokenInicio();
		ExpresionRelacional expR = esExpresionRelacional();
		if (expR != null) {
			return new TerminoExpLogica(null, null, expR, null, null);
		} else {
			recuperarTokenInicio();
		}

		guardarTokenInicio();
		LlamadaAMetodo llamada = null;// esLLamada;
		if (llamada != null) {
			return new TerminoExpLogica(null, null, null, llamada, null);
		} else {
			recuperarTokenInicio();
		}

		if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
			Lexema id = tokenActual;
			siguienteToken();
			return new TerminoExpLogica(null, id, null, null, null);
		}

		if (tokenActual.getLexema().equals("(")) {
			guardarTokenInicio();
			siguienteToken();
			ExpresionLogica expresion = esExpresionLogica();
			if (expresion != null) {
				if (tokenActual.getLexema().equals(")")) {
					siguienteToken();
					return new TerminoExpLogica(null, null, null, null,
							expresion);
				} else {
					reportarError("Se esperaba )", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				recuperarTokenInicio();
			}
		}

		return null;
	}

	private ExpresionRelacional esExpresionRelacional() {
		ExpresionAritmetica expresion1 = null;
		ExpresionAritmetica expresion2 = null;
		Lexema operador = null;
		expresion1 = esExpresionAritmetica();
		if (expresion1 != null) {
			if (tokenActual.getTipo().equals(OPRELACIONAL)) {
				operador = tokenActual;
				siguienteToken();
				expresion2 = esExpresionAritmetica();
				if (expresion2 != null) {
					return new ExpresionRelacional(expresion1, operador,
							expresion2);
				} else {
					reportarError("falta expresion aritmetica", tokenActual
							.getFila(), tokenActual.getColumna());
				}
			}
		}
		return null;
	}

	private ExpresionAritmetica esExpresionAritmetica() {
		TerminoProducto termino = null;
		ExpresionAritmetica expresion = null;
		Lexema operador = null;

		termino = esTerminoProducto();
		if (termino != null) {// hay un termino
			if (tokenActual.getLexema().equals("+")
					|| tokenActual.getLexema().equals("-")) {
				operador = tokenActual;
				siguienteToken();
				expresion = esExpresionAritmetica();
				if (expresion != null) {
					return new ExpresionAritmetica(termino, operador, expresion);
				}// de lo contrario ocurre un error
				else {
					reportarError("falta una expresion", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				return new ExpresionAritmetica(termino, null, null);
			}
		}

		// comprobar primero si hay un negativo precediendo una expresion
		if (tokenActual.getLexema().equals("-")) {
			operador = tokenActual;
			siguienteToken();
			expresion = esExpresionAritmetica();
			if (expresion != null) {
				return new ExpresionAritmetica(termino, operador, expresion);
			}// de lo contrario ocurre un error
			else {
				reportarError("falta una expresion", tokenActual.getFila(),
						tokenActual.getColumna());
			}
		}

		if (tokenActual.getLexema().equals("(")) {
			guardarTokenInicio();
			siguienteToken();
			expresion = esExpresionAritmetica();
			if (expresion != null) {
				if (tokenActual.getLexema().equals(")")) {
					siguienteToken();
					return expresion;
				} else {
					reportarError("Se esperaba )", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				recuperarTokenInicio();
			}
		}

		return null;
	}

	private TerminoProducto esTerminoProducto() {
		TerminoPotencia terminoPotencia = null;
		TerminoProducto termino = null;
		Lexema operador = null;
		// guardarTokenInicio();
		terminoPotencia = esTerminoPotencia();
		if (terminoPotencia != null) {
			if (tokenActual.getLexema().equals("/")
					|| tokenActual.getLexema().equals("*")
					|| tokenActual.getLexema().equals("%")) {
				operador = tokenActual;
				siguienteToken();
				termino = esTerminoProducto();
				if (termino != null) {
					return new TerminoProducto(termino, operador,
							terminoPotencia);
				} else {// falta terminoProducto
					reportarError("falta termino", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				return new TerminoProducto(null, null, terminoPotencia);
			}
		} else {
			// recuperarTokenInicio();
		}
		return null;
	}

	private TerminoPotencia esTerminoPotencia() {
		Factor factor = null;
		TerminoPotencia termino = null;
		Lexema operador = null;
		guardarTokenInicio();
		factor = esFactor();
		if (factor != null) {
			if (tokenActual.getLexema().equals("^")) {
				operador = tokenActual;
				siguienteToken();
				termino = esTerminoPotencia();
				if (termino != null) {
					return new TerminoPotencia(factor, operador, termino);
				} else {
					reportarError("falta termino", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				return new TerminoPotencia(factor, null, null);
			}
		} else {
			recuperarTokenInicio();
		}
		return null;
	}

	private Factor esFactor() {
		Lexema factor = null;
		LlamadaAMetodo llamada = null;
		ExpresionAritmetica expresion = null;

		if (tokenActual.getTipo().equals(NUMENTERO)
				|| tokenActual.getTipo().equals(NUMREAL)) {
			factor = tokenActual;
			siguienteToken();
			return new Factor(factor);
		}
		guardarTokenInicio();
		// llamada=esLlamadaAMetodo();
		if (llamada != null) {
			return new Factor(llamada);
		} else {
			recuperarTokenInicio();
		}
		if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
			factor = tokenActual;
			siguienteToken();
			return new Factor(factor);
		}

		if (tokenActual.getLexema().equals("(")) {
			guardarTokenInicio();
			siguienteToken();
			expresion = esExpresionAritmetica();
			if (expresion != null) {
				if (tokenActual.getLexema().equals(")")) {
					siguienteToken();
					return new Factor(expresion);
				} else {
					reportarError("Se esperaba )", tokenActual.getFila(),
							tokenActual.getColumna());
				}
			} else {
				recuperarTokenInicio();
			}
		}

		return null;
	}

	/**
	 * metodo predicado que identifica si es una lista de parametros
	 *
	 * @param parametros
	 * @return
	 */
	private ArrayList<Parametro> sonParametros(ArrayList<Parametro> parametros) {
		Lexema tipoDato = null;
		Lexema identificador = null;
		String tipo = tokenActual.getTipo();
		if (tipo.equals(IDCLASE) || tipo.equals(INT) || tipo.equals(DOUBLE)
				|| tipo.equals(BOOL) || tipo.equals(STRING)) {
			tipoDato = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
				identificador = tokenActual;
				parametros.add(new Parametro(tipoDato, identificador));
				siguienteToken();
				if (tokenActual.getLexema().equals(",")) {
					siguienteToken();
					return sonParametros(parametros);
				}
				return parametros;
			}
		}
		return null;
	}

	/**
	 * metodo predicado que comprueba si se acepta un bloque de campos
	 *
	 * @return
	 */
	private ArrayList<Campo> esBloqueCampos() {
		if (tokenActual.getTipo().equals(FIELDSBLOCK)) {
			siguienteToken();
			if (esSeparadorSentencias()) {
				ArrayList<Campo> campos = sonCampos(new ArrayList<Campo>());
				if (tokenActual.getTipo().equals(ENDFIELDSBLOCK)) {
					siguienteToken();
					esSeparadorSentencias();
					return campos;
				}
			}
		}
		return null;
	}

	/**
	 * metodo predicado que comprueba si la gramatica soncampos es aceptada,
	 *
	 * @param campos
	 * @return
	 */
	private ArrayList<Campo> sonCampos(ArrayList<Campo> campos) {
		Lexema idCampo = null;
		Lexema acceso = null;
		Lexema tipoDato = null;
		Lexema modificador = null;
		Lexema valor = null;
		if (tokenActual.getTipo().equals(PUBLIC)
				|| tokenActual.getTipo().equals(PRIVATE)
				|| tokenActual.getTipo().equals(PROTECTED)) {
			acceso = tokenActual;
			siguienteToken();
		}
		if (tokenActual.getTipo().equals(STATIC)) {
			modificador = tokenActual;
			siguienteToken();
		}
		String tipo = tokenActual.getTipo();
		if (tipo.equals(IDCLASE) || tipo.equals(INT) || tipo.equals(DOUBLE)
				|| tipo.equals(BOOL) || tipo.equals(STRING)) {
			tipoDato = tokenActual;
			siguienteToken();
			if (tokenActual.getTipo().equals(IDENTIFICADOR)) {
				idCampo = tokenActual;
				siguienteToken();
				if (tokenActual.getLexema().equals("=")) {// opcional
					// una
					// asignacion
					// System.out.println("=");
					siguienteToken();
					if (tokenActual.getTipo().equals(TRUE)
							|| tokenActual.getTipo().equals(FALSE)
							|| tokenActual.getTipo().equals(NUMENTERO)
							|| tokenActual.getTipo().equals(NUMREAL)
							|| tokenActual.getTipo().equals(CADENA)
							|| tokenActual.getTipo().equals(IDENTIFICADOR)) {

						valor = tokenActual;
						siguienteToken();
					}
				}
				if (esSeparadorSentencias()) {
					Campo campo = new Campo(acceso, modificador, tipoDato,
							idCampo, valor, clase);
					campos.add(campo);
					return sonCampos(campos);
				}
			}
		}
		if (campos.size() > 0) {
			return campos;
		} else {
			return null;
		}
	}

	/**
	 * metodo predicado que comprueba si la gramatica implementaciones es
	 * aceptada, <implementaciones>::= IDCLASE ["," <implementaciones> ]
	 *
	 * @param implementaciones
	 * @return un arraylist de implementaciones
	 */
	private ArrayList<Lexema> sonImplementaciones(
			ArrayList<Lexema> implementaciones) {
		if (tokenActual.getTipo().equals(IDCLASE)) {
			implementaciones.add(tokenActual);
			siguienteToken();
			if (tokenActual.getLexema().equals(",")) {
				siguienteToken();
				return sonImplementaciones(implementaciones);
			}
			return implementaciones;
		} else {
			reportarError("No es una implementacion Valida", tokenActual
					.getFila(), tokenActual.getColumna());
			return null;
		}
	}

	/**
	 * metodo predicado para comprobar si cumple con la gramatica importaciones,
	 * <importaciones>::= import <ruta><separadorSentencia>[<importaciones>]
	 *
	 * @param importaciones
	 * @return
	 */
	private ArrayList<Importacion> sonImportaciones(
			ArrayList<Importacion> importaciones) {
		guardarTokenInicio();
		if (tokenActual.getTipo().equals(IMPORT)) {
			siguienteToken();
			ArrayList<Lexema> ruta = esRuta(new ArrayList<Lexema>());
			if (ruta != null) {
				Lexema idClase = ruta.get(ruta.size() - 1);
				if (esSeparadorSentencias()) {
					Importacion importacion = new Importacion(idClase, ruta);
					importaciones.add(importacion);
					return sonImportaciones(importaciones);
				}
			}
		}
		if (importaciones.size() > 0) {
			return importaciones;
		} else {
			return null;
		}
	}

	/**
	 * gramatica que consume uno o varios saltos de linea
	 *
	 * @return true si hay almenos 1
	 */
	private boolean esSeparadorSentencias() {
		if (tokenActual.getTipo().equals(SEPARADORSENTENCIA)) {
			siguienteToken();
			return esSeparadorSentencias() || true;
		} else {
			return false;
		}
	}

	/**
	 * Metodo predicado para comprobar si cumple con la gramatica paquete,
	 * <paquete>::= package <ruta> SEPARADORSENTENCIA
	 *
	 * @return paquete
	 */
	private Paquete esPaquete() {
		guardarTokenInicio();
		if (tokenActual.getTipo().equals(PACKAGE)) {
			siguienteToken();
			ArrayList<Lexema> ruta = esRuta(new ArrayList<Lexema>());
			if (ruta != null) {
				if (esSeparadorSentencias()) {
					Paquete paquete = new Paquete(ruta);
					return paquete;
				}
			}
		}
		return null;
	}

	/**
	 * Metodo predicado para comprobar si un conjunto de tokens aprueba la
	 * gramatica Ruta, <ruta>::= IDCLASE["."<ruta>]
	 *
	 * @param ruta
	 * @return una lista de Paquetes
	 */
	private ArrayList<Lexema> esRuta(ArrayList<Lexema> ruta) {
		if (tokenActual.getTipo().equals(IDCLASE)) {
			ruta.add(tokenActual);
			siguienteToken();
			if (tokenActual.getLexema().equals(".")) {
				siguienteToken();
				return esRuta(ruta);
			}
			return ruta;
		} else {
			reportarError("No es una ruta valida", tokenActual.getFila(),
					tokenActual.getColumna());
			return null;
		}
	}

	/**
	 * reporta un error con una sugerencia y la posicion en el codigo donde se
	 * origino el error
	 *
	 * @param sugerencia
	 * @param fila
	 * @param columna
	 */
	private void reportarError(String sugerencia, int fila, int columna) {
		errores.add(sugerencia + " - " + fila + " - " + columna + "\n");
		/*
		 * System.out.println(sugerencia + " -> fila: " + fila + " columna: " +
		 * columna);
		 */
	}

	/**
	 * obtiene el siguiente token del codigo fuente
	 */
	private void siguienteToken() {
		posicion++;
		tokenActual = tablaDeSimbolos.get(posicion);
		// System.out.println(tokenActual.getLexema());
	}

	/**
	 * salvamos un punto en el codigo para empezar a reconocer otra gramatica si
	 * es necesario
	 */
	private void guardarTokenInicio() {
		pila.push(posicion);
	}

	/**
	 * se recupera el token guardado para reconocer otra gramatica
	 */
	private void recuperarTokenInicio() {
		posicion = (Integer) pila.pop();
		tokenActual = tablaDeSimbolos.get(posicion);
	}

	public ArrayList<String> getErrores() {
		return errores;
	}

	public DefaultMutableTreeNode getSubArbol() {
		return unidadDeCompilacion;
	}

	public UnidadCompilacion getUnidadDeCompilacion() {
		return uc;
	}
}